package org.orbis.communicate.logging;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import org.orbis.communicate.MainService;
import org.orbis.communicate.deleter.DeleterPackage;
import org.orbis.index.IndexingPackage;
import org.orbis.structure.ContentInfo;
import org.orbis.toolbox.ByteConverter;

public class TopLogger implements Runnable{
	public static final byte TOPLOG_INDEX = 0x00;
	public static final byte TOPLOG_DELETE = 0x02;

	private File logFile;
	private FileInputStream reliefStream;
	private FileOutputStream logStream;

	public TopLogger(String fileName) throws IOException{
		logFile = new File(fileName);
//		System.out.println("[DEBUG] Log file: " + logFile.getAbsolutePath() + " Exist? " + logFile.exists());
		if(logFile.exists()){
			reliefStream = new FileInputStream(logFile);
		}else{
			logFile.createNewFile();
			logStream = new FileOutputStream(logFile);
		}
	}

	public boolean isBackupAvailable(){
		return reliefStream != null;
	}

	public int nextType() throws IOException{
		if(reliefStream == null) return -1;
		return reliefStream.read();
	}

	public boolean isReliefAvailable() throws IOException{
		return reliefStream.available() != 0;
	}

	public void endRelief(){
		try{
			if(reliefStream != null){
				reliefStream.close();
				logFile.delete();
				
				logFile.createNewFile();
				logStream = new FileOutputStream(logFile);
			}
		}catch(IOException e){
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public IndexingPackage reliefIndex() throws IOException{
		ContentInfo contentInfo = MainService.getInstance().contentInfo();
		IndexingPackage pkg = new IndexingPackage(contentInfo);
		byte[] b;

//		reliefStream.read(); // 1b
		b = new byte[8];
		reliefStream.read(b);
		pkg.setID(ByteConverter.byteToLong(b));

		// Index incompatibility
		if(reliefStream.read() != contentInfo.contentsNum()) return null;
		if(reliefStream.read() != contentInfo.appendixNum()) return null;
		if(reliefStream.read() != contentInfo.binaryFlagNum()) return null;
		if(reliefStream.read() != contentInfo.integerFlagNum()) return null;

		for(int i = 0; i < contentInfo.contentsNum(); i++){
			b = new byte[2];
			reliefStream.read(b);
			b = new byte[ByteConverter.byteToInt(b)];
			reliefStream.read(b);
			pkg.setContent(new String(b, Charset.forName("UTF-8")), i);
		}

		for(int i = 0; i < contentInfo.appendixNum(); i++){
			b = new byte[2];
			reliefStream.read(b);
			b = new byte[ByteConverter.byteToInt(b)];
			reliefStream.read(b);
			pkg.setAppendix(new String(b, Charset.forName("UTF-8")), i);
		}

		for(int i = 0; i < contentInfo.binaryFlagNum(); i++){
			pkg.setBinaryFlag(reliefStream.read() == 1? true: false, i);
		}

		b = new byte[4];
		for(int i = 0; i < contentInfo.integerFlagNum(); i++){
			reliefStream.read(b);
			pkg.setIntFlag(ByteConverter.byteToInt(b), i);
		}

		return pkg;
	}

	public boolean addIndex(IndexingPackage pkg) throws IOException{
		logStream.write(TOPLOG_INDEX); // 1b, 0x00 means Index
		logStream.write(ByteConverter.longToByte(pkg.ID(), 8)); // 8b
		logStream.write(pkg.contentLength()); // 1b
		logStream.write(pkg.appendixLength()); // 1b
		logStream.write(pkg.binaryFlagLength()); // 1b
		logStream.write(pkg.intFlagLength()); // 1b

		for(int i = 0; i < pkg.contentLength(); i++){
			byte[] b = pkg.content(i).getBytes(Charset.forName("UTF-8"));
			logStream.write(ByteConverter.intToByte(b.length, 2)); // 2b
			logStream.write(b);
		}

		for(int i = 0; i < pkg.appendixLength(); i++){
			byte[] b = pkg.appendix(i).getBytes(Charset.forName("UTF-8"));
			logStream.write(ByteConverter.intToByte(b.length, 2));
			logStream.write(b);
		}

		for(int i = 0; i < pkg.binaryFlagLength(); i++){
			logStream.write(pkg.binaryFlag(i)? 1: 0);
		}

		for(int i = 0; i < pkg.intFlagLength(); i++){
			logStream.write(ByteConverter.intToByte(pkg.intFlag(i), 4));
		}

		return true;
	}
	
	public DeleterPackage reliefDelete(int type) throws IOException {
		// TODO: 
//		byte[] b;
//		switch(type) {
//		case 0x10:
//			b = new byte[8];
//			reliefStream.read(b);
//			long l = ByteConverter.byteToLong(b);
//			return new DeleterPackage(l);
//		case 0x20:
//			b = new byte[4];
//			reliefStream.read(b);
//			int i = ByteConverter.byteToInt(b);
//			return new DeleterPackage(i);
//		case 0x40:
//			b = new byte[2];
//			reliefStream.read(b);
//			int length = ByteConverter.byteToInt(b);
//			b = new byte[length];
//			String str = new String(b, Charset.forName("UTF-8"));
//			return new DeleterPackage(str);
//		default:
//			return null;
//		}
		return null;
	}

	public boolean addDelete(DeleterPackage pkg) throws IOException{
		// TODO: 
//		if(pkg.type() == DeleterPackage.TYPE_ID){
//			logStream.write(0x10 + TOPLOG_DELETE); // 0x12 means Delete with Key length = 8
//			logStream.write(ByteConverter.longToByte(pkg.ID(), 8));
//		}else if(pkg.type() == DeleterPackage.TYPE_FLAG){
//			logStream.write(0x20 + TOPLOG_DELETE); // 0x22 means Delete with Key length = 4
//			logStream.write(ByteConverter.longToByte(pkg.flagval(), 4));
//		}else if(pkg.type() == DeleterPackage.TYPE_CONTENT){
//			logStream.write(0x40 + TOPLOG_DELETE); // 0x42 means Delete with Key length's not fixed
//			byte[] b = pkg.strval().getBytes(Charset.forName("UTF-8"));
//			logStream.write(ByteConverter.intToByte(b.length, 2));
//			logStream.write(b);
//		}

		return true;
	}

	public void closeFile() throws IOException{
		if(logStream != null) logStream.close();
		logFile.delete();
	}

	@Override
	protected void finalize() throws Throwable{
		if(logStream != null) logStream.close();
	}

	@Override
	public void run(){

	}
}
